﻿using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Security;
using System.Windows;

namespace Gencode.VECUtil
{
    public delegate void SaveFileEventHandler(object sender, SerializationEventArgs e);
    public delegate void LoadFileEventHandler(object sender, SerializationEventArgs e);
    public delegate void OpenFileEventHandler(object sender, OpenFileEventArgs e);

    public class FileManager
    {
        public event SaveFileEventHandler SaveFile;
        public event LoadFileEventHandler LoadFile;
        public event OpenFileEventHandler OpenFile;
        public event EventHandler ClearEvent;
        public event EventHandler DocChangedEvent;

        string fileName = "";
        string fileDlgInitDir = "";
        string newDocName;
        Window frmOwner;

        /// <summary>
        /// Enumeration used for Save function
        /// </summary>
        public enum SaveType
        {
            Save,
            SaveAs
        }

        public FileManager(Window mainForm)
        {
            frmOwner = mainForm;
            frmOwner.Closing += OnClosing;

            //updateTitle = data.UpdateTitle;

            //newDocName = data.NewDocName;

            //fileDlgFilter = data.FileDialogFilter;

            //registryPath = data.RegistryPath;

            //if (!registryPath.EndsWith("\\"))
            //    registryPath += "\\";

            //registryPath += "FileDir";

            //// attempt to read initial directory from registry
            //RegistryKey key = Registry.CurrentUser.OpenSubKey(registryPath);

            //if (key != null)
            //{
            //    string s = (string)key.GetValue(registryValue);

            //    if (!Empty(s))
            //        fileDlgInitDir = s;
            //}
        }

        //public DocumentManager(DocManagerData data)
        //{
        //    frmOwner = data.FormOwner;
        //    frmOwner.Closing += OnClosing;

        //    updateTitle = data.UpdateTitle;

        //    newDocName = data.NewDocName;

        //    fileDlgFilter = data.FileDialogFilter;

        //    registryPath = data.RegistryPath;

        //    if (!registryPath.EndsWith("\\"))
        //        registryPath += "\\";

        //    registryPath += "FileDir";

        //    // attempt to read initial directory from registry
        //    RegistryKey key = Registry.CurrentUser.OpenSubKey(registryPath);

        //    if (key != null)
        //    {
        //        string s = (string)key.GetValue(registryValue);

        //        if (!Empty(s))
        //            fileDlgInitDir = s;
        //    }
        //}


        /// <summary>
        /// Open document
        /// </summary>
        /// <param name="newFileName">
        /// Document file name. Empty - function shows Open File dialog.
        /// </param>
        /// <returns></returns>
        public bool OpenDocument(string newFileName)
        {
            // Check if we can close current file
            //if (!CloseDocument())
            //    return false;

            if (string.IsNullOrEmpty(newFileName))
            {
                Microsoft.Win32.OpenFileDialog openFileDialog1 = new Microsoft.Win32.OpenFileDialog();
                openFileDialog1.Filter = "VecDraw files (.vec)|*.vec";
                openFileDialog1.InitialDirectory = fileDlgInitDir;

                Nullable<bool> result = openFileDialog1.ShowDialog(frmOwner);
                if (result == false)
                    return false;

                newFileName = openFileDialog1.FileName;
                fileDlgInitDir = new FileInfo(newFileName).DirectoryName;
            }

            // Read the data
            try
            {
                using (Stream stream = new FileStream(
                           newFileName, FileMode.Open, FileAccess.Read))
                {
                    // Deserialize object from text format
                    IFormatter formatter = new BinaryFormatter();

                    if (LoadFile != null)        // if caller subscribed to this event
                    {
                        SerializationEventArgs args = new SerializationEventArgs(
                            formatter, stream, newFileName);

                        // raise event to load document from file
                        LoadFile(this, args);

                        if (args.Error)
                        {
                            // report failure
                            if (OpenFile != null)
                            {
                                OpenFile(this,
                                    new OpenFileEventArgs(newFileName, false));
                            }

                            return false;
                        }

                        // raise event to show document in the window
                        if (DocChangedEvent != null)
                        {
                            DocChangedEvent(this, new EventArgs());
                        }
                    }
                }
            }
            // Catch all exceptions which may be raised from this code.
            // Caller is responsible to handle all other exceptions 
            // in the functions invoked by LoadEvent and DocChangedEvent.
            catch (ArgumentNullException ex) { return HandleOpenException(ex, newFileName); }
            catch (ArgumentOutOfRangeException ex) { return HandleOpenException(ex, newFileName); }
            catch (ArgumentException ex) { return HandleOpenException(ex, newFileName); }
            catch (SecurityException ex) { return HandleOpenException(ex, newFileName); }
            catch (FileNotFoundException ex) { return HandleOpenException(ex, newFileName); }
            catch (DirectoryNotFoundException ex) { return HandleOpenException(ex, newFileName); }
            catch (PathTooLongException ex) { return HandleOpenException(ex, newFileName); }
            catch (IOException ex) { return HandleOpenException(ex, newFileName); }

            // Clear dirty bit, cache the file name and set the caption
            //Dirty = false;
            //SetFileName(newFileName);
            this.fileName = newFileName;

            if (OpenFile != null)
            {
                // report success
                OpenFile(this, new OpenFileEventArgs(newFileName, true));
            }

            // Success
            return true;
        }


        /// <summary>
        /// Save file.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public bool SaveDocument(SaveType type)
        {
            string newFileName = this.fileName;

            Microsoft.Win32.SaveFileDialog saveFileDialog1 = new Microsoft.Win32.SaveFileDialog();
            saveFileDialog1.FileName = "Document"; // Default file name
            saveFileDialog1.DefaultExt = ".vec"; // Default file extension
            saveFileDialog1.Filter = "VecDraw files (.vec)|*.vec";//|Text document (.txt)|*.txt|JPG (.jpg)|*.jpg"; // Filter files by extension

            if ((type == SaveType.SaveAs) ||
                string.IsNullOrEmpty(newFileName))
            {

                if (!string.IsNullOrEmpty(newFileName))
                {
                    saveFileDialog1.InitialDirectory = Path.GetDirectoryName(newFileName);
                    saveFileDialog1.FileName = Path.GetFileName(newFileName);
                }
                else
                {
                    saveFileDialog1.InitialDirectory = fileDlgInitDir;
                    saveFileDialog1.FileName = newDocName;
                }

                Nullable<bool> result = saveFileDialog1.ShowDialog(frmOwner);

                if (result == false)
                    return false;

                string filename = saveFileDialog1.FileName;
                
                newFileName = saveFileDialog1.FileName;
                fileDlgInitDir = new FileInfo(newFileName).DirectoryName;
            }

            // Write the data
            try
            {
                using (Stream stream = new FileStream(
                           newFileName, FileMode.Create, FileAccess.Write))
                {
                    // Serialize object to text format
                    IFormatter formatter = new BinaryFormatter();

                    if (SaveFile != null)        // if caller subscribed to this event
                    {
                        SerializationEventArgs args = new SerializationEventArgs(
                            formatter, stream, newFileName);

                        // raise event
                        SaveFile(this, args);

                        if (args.Error)
                            return false;
                    }

                }
            }
            catch (ArgumentNullException ex) { return HandleSaveException(ex, newFileName); }
            catch (ArgumentOutOfRangeException ex) { return HandleSaveException(ex, newFileName); }
            catch (ArgumentException ex) { return HandleSaveException(ex, newFileName); }
            catch (SecurityException ex) { return HandleSaveException(ex, newFileName); }
            catch (FileNotFoundException ex) { return HandleSaveException(ex, newFileName); }
            catch (DirectoryNotFoundException ex) { return HandleSaveException(ex, newFileName); }
            catch (PathTooLongException ex) { return HandleSaveException(ex, newFileName); }
            catch (IOException ex) { return HandleSaveException(ex, newFileName); }

            // Clear the dirty bit, cache the new file name
            // and the caption is set automatically
            //Dirty = false;
            //SetFileName(newFileName);
            this.fileName = newFileName;

            // Success
            return true;
        }

        #region Exception Methods


        /// <summary>
        /// Handle exception in OpenDocument function
        /// </summary>
        /// <param name="ex"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private bool HandleOpenException(Exception ex, string fileName)
        {
            MessageBox.Show(frmOwner,
                "Open File operation failed. File name: " + fileName + "\n" +
                "Reason: " + ex.Message,
                ""/*Application.ProductName*/);

            if (OpenFile != null)
            {
                // report failure
                OpenFile(this, new OpenFileEventArgs(fileName, false));
            }

            return false;
        }

        /// <summary>
        /// Handle exception in SaveDocument function
        /// </summary>
        /// <param name="ex"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private bool HandleSaveException(Exception ex, string fileName)
        {
            MessageBox.Show(frmOwner,
                "Save File operation failed. File name: " + fileName + "\n" +
                "Reason: " + ex.Message,
                ""/*Application.ProductName*/);

            return false;
        }

        #endregion 

        /// <summary>
        /// Save initial directory to the Registry
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            //RegistryKey key = Registry.CurrentUser.CreateSubKey(registryPath);
            //key.SetValue(registryValue, fileDlgInitDir);
        }
    }

    #region Class SerializationEventArgs

    /// <summary>
    /// Serialization event arguments.
    /// Used in events raised from DocManager class.
    /// Class contains information required to load/save file.
    /// </summary>
    public class SerializationEventArgs : System.EventArgs
    {
        private IFormatter formatter;
        private Stream stream;
        private string fileName;
        private bool errorFlag;

        public SerializationEventArgs(IFormatter formatter, Stream stream,
            string fileName)
        {
            this.formatter = formatter;
            this.stream = stream;
            this.fileName = fileName;
            errorFlag = false;
        }

        public bool Error
        {
            get
            {
                return errorFlag;
            }
            set
            {
                errorFlag = value;
            }
        }

        public IFormatter Formatter
        {
            get
            {
                return formatter;
            }
        }

        public Stream SerializationStream
        {
            get
            {
                return stream;
            }
        }
        public string FileName
        {
            get
            {
                return fileName;
            }
        }
    }

    #endregion

    #region Class OpenFileEventArgs

    /// <summary>
    /// Open file event arguments.
    /// Used in events raised from DocManager class.
    /// Class contains name of file and result of Open operation.
    /// </summary>
    public class OpenFileEventArgs : System.EventArgs
    {
        private string fileName;
        private bool success;

        public OpenFileEventArgs(string fileName, bool success)
        {
            this.fileName = fileName;
            this.success = success;
        }

        public string FileName
        {
            get
            {
                return fileName;
            }
        }

        public bool Succeeded
        {
            get
            {
                return success;
            }
        }
    }

    #endregion

}
